#include "GribWrapper.h"

namespace OCPP
{
	GribWrapper::GribWrapper()
	{

	}

	GribWrapper::~GribWrapper()
	{
		close();
	}

	void GribWrapper::close()
	{
		CORE_LOCK_MUTEX(GribSyncFile::intanceMutex);
		if (_NcSyncFile.use_count() == 2)
		{
			GribSyncFile::instances.erase(_filName);
		}
	}

	bool GribWrapper::open(String fileName)
	{
		CORE_LOCK_MUTEX(GribSyncFile::intanceMutex);
		_filName = fileName;
		auto it = GribSyncFile::instances.find(fileName);
		if (it != GribSyncFile::instances.end())
		{
			_NcSyncFile = it->second;
			return true;
		}

		try {
			_NcSyncFile = std::make_shared<GribSyncFile>();
			if (_NcSyncFile->open(fileName)) {
				GribSyncFile::instances[fileName] = _NcSyncFile;
				return true;
			}
		}
		catch (...) {
			OCPP_LOG_ERROR << "unknow error.\n";
		}

		_filName = "";
		_NcSyncFile = nullptr;
		return false;
	}

	GribVariablePtr GribWrapper::findVariable(String name)
	{
		auto variable = _NcSyncFile->getVar(name);
		if (variable != nullptr) {
			variable->gridSyncFilePtr = _NcSyncFile;
		}
		return variable;
	}

	Json::Value GribWrapper::getVars()
	{
		Json::Value value;

		auto mVariableMap = _NcSyncFile->getVars();
		auto it = mVariableMap.begin();
		for (; it != mVariableMap.end(); it++)
		{
			const auto& var = it->second;
			Json::Value value1;
			value1["name"] = var->getName();

			const auto& times = var->getTimeVec();
			if (times.size() > 0)
			{
				for (const auto& time : times)
				{
					value1["time"].append(time.toString());
				}

				const auto& levels = var->getLevelVec(times[0]);
				StringVector sv;
				for (const auto& [GribLevel, size_t] : levels)
				{
					sv.push_back(GribLevel.toString());
				}

				bool unitPa = StringUtil::contains(var->getName(), "isobaric", false);
				if (unitPa)
				{
					std::sort(sv.begin(), sv.end(),
						[](const String& x, const String& y)
						{
							return std::stof(x) > std::stof(y);
						}
					);
				}

				for (const auto& s : sv)
				{
					value1["level"].append(s);
				}
			}
			value.append(value1);
		}

		return value;
	}

	void GribWrapper::print()
	{
		auto mVariableMap = _NcSyncFile->getVars();
		auto it = mVariableMap.begin();
		for (; it != mVariableMap.end(); it++)
		{
			auto var = it->second.get();
			OCPP_LOG_INFO << var->description() << "\n";
		}
	}

	GribReadIndexList GribWrapper::read(String varName, int levelIndex, int timeIndex, const VolumeOptions& options)
	{
		GribReadIndexList list;

		auto variable = _NcSyncFile->getVar(varName);
		if (variable == nullptr)
		{
			OCPP_LOG_ERROR << "var not exist:" << varName << "\n";
		}
		else
		{
			auto timeVec = variable->getTimeVec();
			if (timeIndex > -1 && timeIndex < timeVec.size())
			{
				list.push_back({ variable ,timeVec[timeIndex] ,levelIndex });
			}
			else
			{
				for (const auto& time : timeVec)
				{
					list.push_back({ variable ,time ,levelIndex });
				}
			}
		}

		return list;
	}

	GribOutput GribWrapper::getData(const GribReadIndex& index, const VolumeOptions& options)
	{
		auto& [variable, time, levelIndex] = index;
		return _NcSyncFile->getData(variable, time, levelIndex, options);
	}

	void GribWrapper::extract(String varName, int levelIndex, int timeIndex, const VolumeOptions& options)
	{
		auto list = read(varName, levelIndex, timeIndex, options);
		auto& writer = options.getWriter();
		for (auto& index : list)
		{
			auto gribOutput = getData(index, options);
			gribOutput.ClipIf(options);
			gribOutput.addDimension(varName);
			writer.write(gribOutput, options);
		}
	}
}